package com.example.gbuffer.Component;

import android.content.Context;
import android.opengl.GLES30;
import android.opengl.GLES32;
import android.util.Log;

import com.example.gbuffer.Utils.FileUtil;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;

public class Shader {

    public Shader(){};
    private final String LOG_TAG = "Shader";
    private int m_program_id = -1;
    private String m_name = "GLShader";

    /**
     * @brief 使用当前着色器。
     */
    public void use() {
        GLES32.glUseProgram(m_program_id);
    }

    public boolean makeShaderProgramFromAssets(Context context, String vsFileName, String fsFileName){

        String vsContent = FileUtil.readFileFromAssets(context, vsFileName);
        String fsContent = FileUtil.readFileFromAssets(context, fsFileName);

        m_program_id = createAndCompileShaders(vsContent, fsContent);
        return m_program_id >= 0;
    }

    /**
     * @brief 创建并编译着色器程序
     * @param vsCode 顶点着色器源代码
     * @param fsCode 片段着色器源代码
     * @return 着色器ID（正整数），如果id为-1代表创建失败。
     */
    private int createAndCompileShaders(String vsCode, String fsCode) {

        int vertexShader;
        try {
            vertexShader = compileShader(GLES32.GL_VERTEX_SHADER, vsCode);
        }catch (RuntimeException ex) {
            Log.e(LOG_TAG, m_name + " load vertex shader failed: " + ex.getMessage());
            return -1;
        }

        int fragmentShader;
        try {
            fragmentShader = compileShader(GLES32.GL_FRAGMENT_SHADER, fsCode);
        }catch (RuntimeException ex) {
            Log.e(LOG_TAG, m_name + " load fragment shader failed: " + ex.getMessage());
            return -1;
        }

        // 创建程序并链接
        int programId = GLES32.glCreateProgram();
        GLES32.glAttachShader(programId, vertexShader);
        GLES32.glAttachShader(programId, fragmentShader);
        GLES32.glLinkProgram(programId);

        // 检查程序
        int[] linkStatus = new int[1];
        GLES32.glGetProgramiv(programId, GLES32.GL_LINK_STATUS, linkStatus, 0);
        if (linkStatus[0] == GLES32.GL_FALSE) {
            String error = GLES32.glGetProgramInfoLog(programId);
            Log.e(LOG_TAG, m_name + " link program failed: " + error);
            return -1;
        }

        // 清理着色器
        GLES32.glDeleteShader(vertexShader);
        GLES32.glDeleteShader(fragmentShader);

        return programId;
    }

    /**
     * @brief 创建并编译着色器程序
     * @param vsCode 顶点着色器源代码
     * @param gsCode 几何着色器源代码
     * @param fsCode 片段着色器源代码
     * @return 着色器ID
     */
    private int createAndCompileShaders(String vsCode, String gsCode, String fsCode) {

        int vertexShader = compileShader(GLES32.GL_VERTEX_SHADER, vsCode);
        int geometryShader = compileShader(GLES32.GL_GEOMETRY_SHADER, gsCode);
        int fragmentShader = compileShader(GLES32.GL_FRAGMENT_SHADER, fsCode);

        // 创建程序并链接
        int programId = GLES32.glCreateProgram();
        GLES32.glAttachShader(programId, vertexShader);
        GLES32.glAttachShader(programId, geometryShader);
        GLES32.glAttachShader(programId, fragmentShader);
        GLES32.glLinkProgram(programId);

        // 清理着色器
        GLES32.glDeleteShader(vertexShader);
        GLES32.glDeleteShader(geometryShader);
        GLES32.glDeleteShader(fragmentShader);

        return programId;
    }

    /**
     * @brief 编译给定的着色器代码
     * @param type 着色器类型
     * @param shaderCode 着色器源码
     * @return 着色器ID
     * @exception RuntimeException 编译失败，message = 失败原因。
     */
    private int compileShader(int type, String shaderCode) {
        int shader = GLES32.glCreateShader(type);
        GLES32.glShaderSource(shader, shaderCode);
        GLES32.glCompileShader(shader);

        // 检查编译状态
        int[] compiled = new int[1];
        GLES32.glGetShaderiv(shader, GLES32.GL_COMPILE_STATUS, compiled, 0);
        if (compiled[0] == GLES32.GL_FALSE) {
            String error = GLES32.glGetShaderInfoLog(shader);
            GLES32.glDeleteShader(shader); // 销毁着色器
            throw new RuntimeException(error);
        }

        return shader;
    }

    /**
     * @brief 设置mat4 float元素类型的uniform
     * @param uniformName 统一变量的名称
     * @param matrix 矩阵（必须是列主序）
     */
    public void setUniformMatrix4fv(String uniformName, float[] matrix){
        int valueLocation = GLES30.glGetUniformLocation(m_program_id, uniformName);
        if (valueLocation == -1) { logError(uniformName);}
        GLES32.glUniformMatrix4fv(valueLocation, 1, false, matrix, 0);
    }

    public void setUniform1fv(String uniformName, float value){
        int valueLocation = GLES30.glGetUniformLocation(m_program_id, uniformName);
        if (valueLocation == -1) { logError(uniformName);}

        GLES32.glUniform1f(valueLocation, value);
    }

    public void setUniform3fv(String uniformName, float[] values){
        int valueLocation = GLES30.glGetUniformLocation(m_program_id, uniformName);
        if (valueLocation == -1) { logError(uniformName);}

        FloatBuffer buffer = FloatBuffer.wrap(values);
        buffer.position(0);
        GLES32.glUniform3fv(valueLocation, 1, buffer);
    }

    public void setUniform1i(String uniformName, int value) {
        int valueLocation = GLES30.glGetUniformLocation(m_program_id, uniformName);
        if (valueLocation == -1) { logError(uniformName);}

        GLES32.glUniform1i(valueLocation, value);
    }

    private void logError(String uniformName) {
        Log.e(LOG_TAG, m_name + " - set uniform failed. uniform name: " + uniformName);
    }

    public String getName() {
        return m_name;
    }

    public void setName(String m_name) {
        this.m_name = m_name;
    }
}
